/*
 * ILinkService.java
 *
 * Version information
 *
 * Date Apr 3, 2006
 * Copyright (c) AmSoft Systems, 2006
 */
package net.amsoft.iservice.ilink.service;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.amsoft.iservice.ilink.hibernate.IBroker;
import net.amsoft.iservice.ilink.hibernate.Redirect;
import net.amsoft.iservice.ilink.hibernate.Synonym;
import net.amsoft.iservice.ilink.util.ILinkHelpers;
import net.amsoft.iservice.util.HibernateUtil;
import net.amsoft.iservice.util.exception.IServiceException;

import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.Transaction;

/**
 * ILinkService class is designed to perform the logic operations. It performs
 * different operations based on the requests from the application layer, these
 * operations are in terms of interacting with database(saving, retreiving,
 * updating data) using the ILinkHelpers class. Hence the class is a major
 * functional unit between application layer and the data (hibernate) layer. The
 * 
 */
public class ILinkService {
	public static final Logger oLogger = Logger.getLogger(ILinkService.class);

	/**
	 * Verifies if the synonym belongs to the same iBroker.
	 * 
	 * @param sIBrokerName
	 *            Name of the Ibroker at which syninym is subscribed
	 * @param sSynonym
	 *            Synonym to be verified
	 * @return true: If the synonym is subscrtibed to the service <BR>
	 *         false: If the synonym is not subscribed to the service
	 * @throws IServiceException
	 *             Thrown when any of the he required paramters have been passed
	 *             as null
	 */
	public static boolean verifySynonym(String sIBrokerName, String sSynonym)
			throws IServiceException {
		oLogger.info("verifySynonym() : entry with data : BrokerName: "
				+ sIBrokerName + ", Synonym: " + sSynonym);
		// Check if any parameter is passed as null
		if (sIBrokerName == null || sSynonym == null) {
			oLogger.warn("verifySynonym() : throw exception, BrokerName: "
					+ sIBrokerName + ", Synonym: " + sSynonym);
			throw new IServiceException(2028, false);
		}
		Session oSession = HibernateUtil.getSessionFactory().openSession();
		Transaction oTransaction = oSession.beginTransaction();
		try {
			return ((ILinkHelpers.fetchIBroker(sIBrokerName, oSession,
					oTransaction).getId().longValue() == ILinkHelpers
					.fetchSynonym(sSynonym, oSession, oTransaction)
					.getSubscriber().getBroker().getId().longValue()) ? true
					: false);
		} finally {
			oSession.close();
		}
	}

	/**
	 * Fetches all the redirects configured for all the synonyms of the logged
	 * in synonym.
	 * 
	 * @param sSynonym
	 *            Logged in synonym, for which redirects are to be fecthed
	 * @throws IServiceException
	 *             Thrown when sSynonym is pass null
	 */
	public static ArrayList getRedirects(String sSynonym)
			throws IServiceException {
		oLogger.info("getRedirects() : entry woth data : Synonym: " + sSynonym);
		// Check if synonym is passed as null
		if (sSynonym == null) {
			oLogger.warn("getRedirects() : throw exception, Synonym: "
					+ sSynonym);
			throw new IServiceException(2028, false);
		}
		Synonym oSynonym = null;
		Session oSession = HibernateUtil.getSessionFactory().openSession();
		Transaction oTransaction = oSession.getTransaction();
		oTransaction.begin();
		try {
			try {
				oSynonym = ILinkHelpers.fetchSynonym(sSynonym, oSession,
						oTransaction);
			} catch (Exception e) {
				oTransaction.rollback();
				oLogger
						.warn("getRedirects() : throw exception, errorcode: 2015");
				throw new IServiceException(oLogger, 2015);
			}
			// Check if redirects are to be fetched for Global I-Number
			if (oSynonym.getGlobalINumber() != null) {
				oSynonym = (Synonym) ILinkHelpers.fetch(Synonym.class, oSynonym
						.getGlobalINumber().getId(), oSession, oTransaction);
			}
			ArrayList alSynonyms = (ArrayList) ILinkHelpers.fetchAllSynonyms(
					oSynonym, oSession, oTransaction);
			ArrayList<Object> alRedirects = new ArrayList<Object>();
			// Iterate over Synonyms hashmap
			for (int iCnt = 0; iCnt < alSynonyms.size(); iCnt++) {
				oSynonym = (Synonym) alSynonyms.get(iCnt);
				Set sRedirect = ILinkHelpers.fetchRedirects(oSynonym
						.getSynonym(), oSession, oTransaction);
				// Iterate for all redirects in the hashset
				for (Iterator iterator = sRedirect.iterator(); iterator
						.hasNext();) {
					// Create Redirect object, and get redirect from the set
					Redirect oRedirect = new Redirect();
					oRedirect = (Redirect) iterator.next();
					oRedirect.setSynonym(oSynonym);
					alRedirects.add(oRedirect);
				}
			}
			oTransaction.commit();
			oLogger
					.info("getRedirects() : exit after commit, return Redirect List: "
							+ alRedirects);
			return alRedirects;
		} finally {
			oSession.close();
		}
	}

	/**
	 * Saves redirects in the database after validating they already does not
	 * exist
	 * 
	 * @param sSynonym
	 *            Updated synonym for which redirect is to be created
	 * @param sTag
	 *            Tag to be created
	 * @param sWebResource
	 *            URL of the web resource
	 * @param sLoggedInSynonym
	 *            Synonym with which user has logged in
	 * @throws IServiceException
	 *             Thrown when a required parameter is sent null
	 */
	public static void createRedirect(String sSynonym, String sTag,
			String sWebResource, boolean bIsPublic, String sLoggedInSynonym)
			throws IServiceException {
		oLogger.info("createRedirect() : entry with data : Synonym: "
				+ sSynonym + ", Tag: " + sTag + ", WebResource: "
				+ sWebResource + ", Logged in Synonym: " + sLoggedInSynonym);
		// Check if any parameter is passed as null
		if (sSynonym == null || sTag == null || sWebResource == null) {
			oLogger.warn("createRedirect() : throw exception, Synonym: "
					+ sSynonym + ", Tag: " + sTag + ", WebResource: "
					+ sWebResource);
			throw new IServiceException(2028, false);
		}
		ArrayList<Synonym> alSynonyms = null;
		Synonym oSynonym = null;
		Session oSession = HibernateUtil.getSessionFactory().openSession();
		Transaction oTransaction = oSession.getTransaction();
		oTransaction.begin();

		// Create array list of synonym objects
		try {
			if (sSynonym.equals("default")) {
				try {
					oSynonym = ILinkHelpers.fetchSynonym(sLoggedInSynonym,
							oSession, oTransaction);
				} catch (Exception e) {
					oTransaction.rollback();
					oLogger.warn("createRedirect() : Exception : "
							+ e.getMessage());
					throw new IServiceException(oLogger, 2015);
				}
				// Check if redirects are to be fetched for Global I-Number
				if (oSynonym.getGlobalINumber() != null) {
					oSynonym = (Synonym) ILinkHelpers.fetch(Synonym.class,
							oSynonym.getGlobalINumber().getId(), oSession,
							oTransaction);
				}
				alSynonyms = (ArrayList<Synonym>) ILinkHelpers
						.fetchAllSynonyms(oSynonym, oSession, oTransaction);
			} else {
				oSynonym = ILinkHelpers.fetchSynonym(sSynonym, oSession,
						oTransaction);
				alSynonyms = new ArrayList<Synonym>();
				alSynonyms.add(oSynonym);
			}
			boolean isError = false;

			// Iterate over synonym objects to validate if redirect is duplicate
			for (Iterator iter = alSynonyms.iterator(); iter.hasNext();) {
				oSynonym = (Synonym) iter.next();
				Redirect oRedirect = ILinkHelpers.fetchRedirectByTag(oSynonym
						.getSynonym(), sTag, oSession, oTransaction);
				if (oRedirect == null) {
					Set oSet = oSynonym.getRedirectSet();
					oRedirect = new Redirect();
					oRedirect.setTag(sTag);
					oRedirect.setWebResource(sWebResource);
					oRedirect.setPublic(bIsPublic);
					oSet.add(oRedirect);
					ILinkHelpers.saveRedirect(oSynonym.getSynonym(), oSet,
							oSession, oTransaction);
				} else {
					isError = true;
				}
			}
			oTransaction.commit();
			if (isError) {
				oLogger.warn("createRedirect() : throw exception, Error:true");
				throw new IServiceException(oLogger, 2037);
			}
		} finally {
			oSession.close();
		}
	}

	/**
	 * Updates the changed i-link properties: i-name, tag name, forwards to url
	 * 
	 * @param sSynonym
	 *            Updated Synonym
	 * @param sTag
	 *            Updated Tag
	 * @param sWebResource
	 *            URL of web resource to which is configured
	 * @param bIsPublic
	 *            access parameter for new redirect
	 * @param sSynonymOld
	 *            Old Synonym
	 * @param sTagOld
	 *            Old Tag
	 * @throws IServiceException
	 *             Thrown when a required parameter is sent null
	 */
	public static void updateRedirect(String sSynonym, String sTag,
			String sWebResource, boolean bIsPublic, String sSynonymOld,
			String sTagOld) throws IServiceException {
		oLogger.info("updateRedirect() : entry with data : Synonym: "
				+ sSynonym + ", Tag: " + sTag + ", WebResource: "
				+ sWebResource + ", IsPublic: " + bIsPublic + ", Old Synonym: "
				+ sSynonymOld + ", Old Tag: " + sTagOld);
		if (sSynonym == null || sTag == null || sWebResource == null
				|| sSynonymOld == null) {
			oLogger.warn("updateRedirect() : throw exception, : Synonym: "
					+ sSynonym + ", Tag: " + sTag + ", WebResource: "
					+ sWebResource + ", Old Synonym: " + sSynonymOld);
			throw new IServiceException(oLogger, 2028);
		}
		Session oSession = HibernateUtil.getSessionFactory().openSession();
		Transaction oTransaction = oSession.beginTransaction();
		try {
			ArrayList<Synonym> alSynonyms = new ArrayList<Synonym>();
			Synonym oSynonym = null;
			if (sSynonym.equalsIgnoreCase("default")) {
				// Case when user edits i-name to "Any"
				oSynonym = ILinkHelpers.fetchSynonym(sSynonymOld, oSession,
						oTransaction);
				alSynonyms = (ArrayList<Synonym>) ILinkHelpers
						.fetchAllSynonyms(oSynonym, oSession, oTransaction);
			} else {
				alSynonyms.add(ILinkHelpers.fetchSynonym(sSynonym, oSession,
						oTransaction));
			}
			boolean isError = false;
			for (Iterator iter = alSynonyms.iterator(); iter.hasNext();) {
				boolean isDuplicate = false;
				oSynonym = (Synonym) iter.next();
				Set oSet = oSynonym.getRedirectSet();
				Redirect oRedirect = null;
				if (sTag.equals(sTagOld)) {
					// may be iname is changed
					if (oSynonym.getSynonym().equals(sSynonymOld)) {
						// there is no change that means update the webresource
						// and access
						oRedirect = ILinkHelpers.fetchRedirectByTag(oSynonym
								.getSynonym(), sTag, oSession, oTransaction);
					} else {
						// check new synonym should not have the given tag, if
						// exists give error otherwise create the tag for new//
						// syn and delete stag for old synonym
						oRedirect = ILinkHelpers.fetchRedirectByTag(oSynonym
								.getSynonym(), sTag, oSession, oTransaction);
						if (oRedirect == null) {
							if (!sSynonym.equalsIgnoreCase("default"))
								ILinkHelpers.fetchSynonym(sSynonymOld,
										oSession, oTransaction)
										.getRedirectSet().remove(
												ILinkHelpers
														.fetchRedirectByTag(
																sSynonymOld,
																sTag, oSession,
																oTransaction));
							oRedirect = new Redirect();
							oRedirect.setSynonym(oSynonym);
						} else {
							oLogger
									.warn("createRedirect() : throw exception, Error:new tag and old tag are same");
							isDuplicate = true;
							isError = true;
						}
					}
				} else {
					// old & new tags are different
					if (oSynonym.getSynonym().equals(sSynonymOld)) {
						// old & new syn are same, check the new tag for new
						// syn- if exists give error, otherwise update
						// tag,webresource and access
						if (ILinkHelpers.fetchRedirectByTag(oSynonym
								.getSynonym(), sTag, oSession, oTransaction) == null) {
							oRedirect = ILinkHelpers.fetchRedirectByTag(
									oSynonym.getSynonym(), sTagOld, oSession,
									oTransaction);
						} else {
							oLogger
									.warn("createRedirect() : throw exception, Error:new tag and old tag are same");
							isDuplicate = true;
							isError = true;
						}
					} else {
						// delete old tag for old syn and create new tag for new
						// synonym
						oRedirect = ILinkHelpers.fetchRedirectByTag(oSynonym
								.getSynonym(), sTag, oSession, oTransaction);
						if (oRedirect == null) {
							if (!sSynonym.equalsIgnoreCase("default"))
								ILinkHelpers.fetchSynonym(sSynonymOld,
										oSession, oTransaction)
										.getRedirectSet().remove(
												ILinkHelpers
														.fetchRedirectByTag(
																sSynonymOld,
																sTagOld,
																oSession,
																oTransaction));
							oRedirect = new Redirect();
							oRedirect.setSynonym(oSynonym);
						} else {
							oLogger
									.warn("createRedirect() : throw exception, Error:new tag and old tag are same");
							isDuplicate = true;
							isError = true;
						}
					}
				}
				if (!isDuplicate) {
					oRedirect.setTag(sTag);
					oRedirect.setWebResource(sWebResource);
					oRedirect.setPublic(bIsPublic);
					oSet.add(oRedirect);
					ILinkHelpers.saveRedirect(oSynonym.getSynonym(), oSet,
							oSession, oTransaction);
				}
			}
			oTransaction.commit();
			if (isError)
				throw new IServiceException(oLogger, 2037);
			oLogger.info("updateRedirect() : exit");
		} finally {
			if (oSession != null)
				oSession.close();
		}
	}

	/**
	 * Deletes redirects in the database
	 * 
	 * @param sSynonym
	 *            Synonym of the link to be deleted
	 * @param sTag
	 *            Tag to be deleted
	 * @throws IServiceException
	 *             Thrown when a required parameter is sent null
	 */
	public static void deleteRedirects(String sSynonym, String sTag)
			throws IServiceException {
		oLogger.info("deleteRedirects() : entry with data : Synonym: "
				+ sSynonym + ", Tag: " + sTag);
		// Check if any parameter is passed as null
		if (sSynonym == null || sTag == null) {
			oLogger.warn("deleteRedirects() : throw exception, Synonym: "
					+ sSynonym + ", Tag: " + sTag);
			throw new IServiceException(oLogger, 2028);
		}
		Session oSession = HibernateUtil.getSessionFactory().openSession();
		Transaction oTransaction = oSession.beginTransaction();
		// Remove redirect
		try {
			ILinkHelpers.removeRedirect(sSynonym, sTag, oSession, oTransaction);
			oTransaction.commit();
			oLogger.info("deleteRedirects() : exit after commit");
		} finally {
			oSession.close();
		}
	}

	/**
	 * Gets the Web resource address configured for slash name corresponding to
	 * the Synonym
	 * 
	 * @param sSynonym
	 *            Authority component of QXRi to be resolved
	 * @param sTag
	 *            Path component of the QXRI to be resolved
	 * @throws IServiceException
	 *             Thrown when a required parameter is sent null
	 */
	public static String getWebresourceAddress(String sSynonym, String sTag)
			throws IServiceException {
		oLogger.info("getWebresourceAddress() : entry with data : Synonym: "
				+ sSynonym + ", Tag: " + sTag);
		// Check if any parameter is passed as null
		if (sSynonym == null || sTag == null) {
			oLogger.warn("getWebresourceAddress() : throw exception, Synonym: "
					+ sSynonym + ", Tag: " + sTag);
			throw new IServiceException(oLogger, 2028);
		}
		Session oSession = HibernateUtil.getSessionFactory().openSession();
		Transaction oTransaction = oSession.beginTransaction();
		try {
			Redirect oRedirect = ILinkHelpers.fetchRedirectByTag(sSynonym,
					sTag, oSession, oTransaction);
			if (oRedirect != null) {
				oTransaction.commit();
				oLogger
						.info("getWebresourceAddress() : exit, return WebResource: "
								+ oRedirect.getWebResource());
				return oRedirect.getWebResource();
			} else {
				oTransaction.rollback();
				oLogger
						.info("getWebresourceAddress() : exit after rollback, return WebResource: null");
				return null;
			}
		} catch (Exception e) {
			oLogger.warn("getWebresourceAddress() : Exception : "
					+ e.getMessage());
		} finally {
			oSession.close();
		}
		oLogger
				.info("getWebresourceAddress() : exit, return WebResource: null");
		return null;
	}

	/**
	 * Gets all the synonyms of the logged in synonym having state active
	 * 
	 * @param sSynonym
	 *            Logged in synonym
	 * @throws IServiceException
	 *             Thrown when the Synonym is passed as null
	 */
	public static String[] getAllSynonyms(String sSynonym)
			throws IServiceException {
		oLogger.info("getAllSynonyms() : entry with data : Synonym: "
				+ sSynonym);
		// Check if synonym is passed as null
		if (sSynonym == null) {
			oLogger.warn("getAllSynonyms() : throw exception, Synonym: "
					+ sSynonym);
			throw new IServiceException(2028, false);
		}
		Session oSession = HibernateUtil.getSessionFactory().openSession();
		Transaction oTransaction = oSession.beginTransaction();
		try {
			Synonym oSynonym = ILinkHelpers.fetchSynonym(sSynonym, oSession,
					oTransaction).getGlobalINumber();
			// Check if Global INumber has been passed
			if (oSynonym == null) {
				oSynonym = ILinkHelpers.fetchSynonym(sSynonym, oSession,
						oTransaction);
			}
			List lSynonyms = ILinkHelpers.fetchAllSynonyms(oSynonym, oSession,
					oTransaction);
			String[] arSynonym = new String[lSynonyms.size()];
			short iSynonym = 0;
			// Iterate over Synonyms to create String array
			for (Iterator iter = lSynonyms.iterator(); iter.hasNext();) {
				oSynonym = (Synonym) iter.next();
				arSynonym[iSynonym++] = oSynonym.getSynonym();
			}
			oTransaction.commit();
			oLogger
					.info("getAllSynonyms() : exit after commit, return Synonym Array: "
							+ arSynonym);
			return arSynonym;
		} finally {
			oSession.close();
		}
	}

	public static Map getIBrokerData(String sIbrokerName)
			throws IServiceException {
		oLogger.info("getIBrokerData() : entry with data : IBrokerName: "
				+ sIbrokerName);
		Map oDataMap = null;
		Session oHibernateSession = null;
		Transaction oHibernateTran = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory().openSession();
			oHibernateSession.beginTransaction();
			IBroker oIBroker = ILinkHelpers.fetchIBroker(sIbrokerName,
					oHibernateSession, oHibernateTran);
			oDataMap = oIBroker.getBrokerData();
			oLogger
					.info("getIBrokerData() : exit, return DataMap: "
							+ oDataMap);
			return oDataMap;
		} catch (Exception e) {
			oLogger.warn("getIBrokerData() : Exception : " + e.getMessage());
		} finally {
			if (oHibernateSession != null)
				oHibernateSession.close();
		}
		oLogger.info("getIBrokerData() : exit, return DataMap: " + oDataMap);
		return oDataMap;
	}

	/**
	 * Returns IBroker object
	 * 
	 * @param ibrokerName
	 * @return IBroker
	 * @throws IServiceException
	 */
	public static IBroker getIBroker(String sIbrokerName)
			throws IServiceException {
		oLogger.info("getIBroker() : entry with data : IBrokerName: "
				+ sIbrokerName);
		IBroker oIBroker = null;
		Session oHibernateSession = null;
		Transaction oHibernateTran = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory().openSession();
			oHibernateSession.beginTransaction();
			oIBroker = ILinkHelpers.fetchIBroker(sIbrokerName,
					oHibernateSession, oHibernateTran);
			oLogger.info("getIBroker() : exit, return IBroker: " + oIBroker);
			return oIBroker;
		} catch (Exception e) {
			oLogger.info("getIBroker() : Exception : " + e.getMessage());
		} finally {
			if (oHibernateSession != null) {
				oHibernateSession.close();
			}
		}
		oLogger.info("getIBroker() : exit, return IBroker: " + oIBroker);
		return oIBroker;
	}

	/**
	 * This method retrieves all the public redirects of the given synonym.
	 * These redirects can be viewed using the default redirect tag "+index"
	 * 
	 * @param sSynonym
	 *            name of synonym
	 * @return List of public redirects
	 */
	public static List<Redirect> getPublicRedirects(String sSynonym) {
		oLogger.info("getPublicRedirects() : entry, with data: Synonym: "
				+ sSynonym);
		List<Redirect> oRedirectList = new ArrayList<Redirect>();
		Session oSession = null;
		Transaction oTransaction = null;
		ArrayList<Synonym> alSynonyms = new ArrayList<Synonym>();
		try {
			oSession = HibernateUtil.getSessionFactory().openSession();
			oTransaction = oSession.beginTransaction();
			Synonym oSynonym = ILinkHelpers.fetchSynonym(sSynonym, oSession,
					oTransaction);
			alSynonyms = (ArrayList<Synonym>) ILinkHelpers.fetchAllSynonyms(
					oSynonym, oSession, oTransaction);
			for (Iterator synIter = alSynonyms.iterator(); synIter.hasNext();) {
				oSynonym = (Synonym) synIter.next();
				Set oRedirectSet = oSynonym.getRedirectSet();
				Redirect oRedirect = new Redirect();
				for (Iterator iter = oRedirectSet.iterator(); iter.hasNext();) {
					oRedirect = (Redirect) iter.next();
					if (oRedirect.getPublic()) {
						oRedirect.setSynonym(oSynonym);
						oRedirectList.add(oRedirect);
						oLogger.debug("getPublicRedirects() : Synonym: "
								+ oRedirect.getSynonym().getSynonym() + ", Tag: "
								+ oRedirect.getTag());						
					}
				}
			}
			oTransaction.commit();
			oLogger.info("getPublicRedirects() : exit, return List"
					+ oRedirectList);
			return oRedirectList;
		} catch (IServiceException e) {
			oLogger.warn("getPublicRedirects() : Exception: " + e.getMessage()
					+ ", return List: null");
			return null;
		} finally {
			oSession.close();
		}
	}

	/**
	 * This method is used to find out whether the given redirect tag is already
	 * defies for the given synonym or not.
	 * 
	 * @param sSynonym
	 *            name of the synonym
	 * @param sTag
	 *            public
	 * @return boolean true: if tag is already defined false: if tag is not
	 *         defined
	 */
	public static boolean isPublicDefined(String sSynonym, String sTag) {
		oLogger.info("isPublicDefined() : entry, with data: Synonym: "
				+ sSynonym + ", Tag: " + sTag);
		Session oSession = null;
		Transaction oTransaction = null;
		try {
			oSession = HibernateUtil.getSessionFactory().openSession();
			oTransaction = oSession.beginTransaction();
			Redirect oRedirect = new Redirect();
			oRedirect = ILinkHelpers.fetchRedirectByTag(sSynonym, sTag,
					oSession, oTransaction);
			if (oRedirect != null) {
				oLogger.info("isPublicDefined() : exit, return true");
				return true;
			} else {
				oLogger.info("isPublicDefined() : exit, return false");
				return false;
			}
		} catch (IServiceException e) {
			oLogger.warn("isPublicDefined() : Exception: " + e.getMessage());
			return false;
		} finally {
			oSession.close();
		}
	}
}